Skip to content

feat: move admin access group grid to mui#911

Open
priscila-moneo wants to merge 2 commits into
masterfrom
feature/move-admin-access-group-grid-mui
Open

feat: move admin access group grid to mui#911
priscila-moneo wants to merge 2 commits into
masterfrom
feature/move-admin-access-group-grid-mui

Conversation

@priscila-moneo

@priscila-moneo priscila-moneo commented May 4, 2026

Copy link
Copy Markdown

ref: https://app.clickup.com/t/86b9n7qe1

Summary by CodeRabbit

  • New Features

    • Create/edit admin access now uses a route-driven modal dialog with add vs. edit automatically determined by the current URL.
  • Bug Fixes

    • Pagination, per-page, sorting, and search state are preserved, with correct reload behavior after save/delete (including edge paging cases).
    • Improved feedback: errors/success now display via snackbar messaging, and create no longer redirects after saving.
  • Refactor

    • List, form, and admin access layout moved to functional/hooks patterns, with the edit page removed in favor of a unified list route.
  • Style

    • UI updated to Material‑UI, with Formik/Yup validation and synced server errors in the form.
  • Tests

    • Expanded coverage for actions, reducer, and list/form interactions.

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Consolidates admin-access create/edit into a dialog on the list page, migrates the form to MUI + Formik with validation, updates actions to include pagination metadata and changes saveAdminAccess behavior, updates reducer pagination/total handling and deletion, removes the legacy edit page, and adds comprehensive tests.

Changes

Admin Access Module Dialog-Based Refactor

Layer / File(s) Summary
Form UI: Formik + MUI validation
src/components/forms/admin-access-form.js
Rewrites form as functional component using useFormik with enableReinitialize, MUI layout, and yup validation schema for title, members, and summits; server errors synced into Formik field errors and touched state via useEffect.
Popup: dialog wiring and save handling
src/pages/admin_access/admin-access-form-popup.js
New Redux-connected popup dialog that renders AdminAccessForm, disables close during isSaving, resets form on close via resetAdminAccessForm, calls onSave(entity), closes on success, and swallows errors in .finally().
Actions: save/get/delete and error handling
src/actions/admin-access-actions.js, src/actions/__tests__/admin-access-actions.test.js
saveAdminAccess removes noAlert parameter, normalizes entity, dispatches startLoading once, uses shared .finally() teardown for create/update flows, and emits snackbarSuccessHandler for both (removing prior history.push redirect on create). getAdminAccesses adds page/perPage to RECEIVE_ADMIN_ACCESSES metadata. getAdminAccess and deleteAdminAccess switch to snackbarErrorHandler. Test suite validates create/update thunks with success and error paths.
Reducer: pagination state and deletion logic
src/reducers/admin_access/admin-access-list-reducer.js, src/reducers/admin_access/__tests__/admin-access-list-reducer.test.js
DEFAULT_STATE gains totalAdminAccesses; REQUEST_ADMIN_ACCESSES updates currentPage/perPage from action payload; RECEIVE_ADMIN_ACCESSES reads pagination metadata and maps payload.data to admin_accesses; ADMIN_ACCESS_DELETED filters deleted item and conditionally decrements totalAdminAccesses; deletion behavior validated by tests.
List page: hooks, table, dialog
src/pages/admin_access/admin-access-list-page.js
Refactors from class/Bootstrap to hooks with MUI Grid2/Button and MuiTable/MuiSearchInput. One useEffect fetches list, another drives popup visibility from route (/new resets and opens; /:access_id loads entity then opens; otherwise closes). Paging/sort/search handlers call getAdminAccesses with updated args; delete computes nextPage for last-item-on-page case and refreshes in .finally(); save calls saveAdminAccess and refreshes; Redux connect injects getAdminAccess/resetAdminAccessForm/saveAdminAccess.
Layout: functional routing and page consolidation
src/layouts/admin-access-layout.js
Converts to functional component; removes EditAdminAccessPage import; routes both /new and /:access_id to AdminAccessListPage; updates fallback Redirect to match.url; simplifies export to wrap layout directly with Restrict.
Tests: list page behavior coverage
src/pages/admin_access/__tests__/admin-access-list-page.test.js
Expanded Jest + RTL suite: mocks actions, MUI table, form, search, and i18n; covers initial render/dialog closed, create button opens, route param opens and fetches, per-page/sort/search args, save-in-flight guard disables controls, save rejection re-enables close without reload, failed detail load doesn't open, route navigation closes dialog, list reload after save/delete/failure, and paging edge case for deleting last item on page >1.
i18n: delete warning and search placeholder
src/i18n/en.json
Updates admin_access.delete_warning to "Please verify you want to delete …" and admin_access.placeholders.search to "Search groups by title".

Sequence Diagram(s)

sequenceDiagram
  participant Router
  participant ListPage as AdminAccessListPage
  participant Actions as admin-access-actions
  participant Reducer as adminAccessListReducer
  participant Dialog as AdminAccessFormPopup
  Router->>ListPage: navigate to /app/admin-access | /new | /:id
  ListPage->>Actions: getAdminAccesses(term, page, perPage, order, orderDir)
  Actions->>Reducer: dispatch REQUEST_ADMIN_ACCESSES + RECEIVE (page/perPage in metadata)
  Reducer->>ListPage: state (admin_accesses, totalAdminAccesses, currentPage, perPage)
  alt Route /new or /:id
    ListPage->>Actions: getAdminAccess(id) if :id
    ListPage->>Dialog: open(entity, errors)
  else List route
    ListPage->>Dialog: close via closeDialog → push /app/admin-access
  end
  Dialog->>Actions: saveAdminAccess(entity) or deleteAdminAccess(id)
  Actions->>Reducer: dispatch ADMIN_ACCESS_ADDED/UPDATED/DELETED
  Actions->>ListPage: refresh via getAdminAccesses in .finally()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • martinquiroga-exo
  • smarcet

Poem

A rabbit taps the keys with flair,
Dialogs bloom in MUI air,
Forms now dance with Formik tunes,
Pages paginate beneath the moon,
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objective of the PR—migrating the admin access group grid UI to Material-UI (MUI)—which aligns with the extensive refactoring work shown across multiple component files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/move-admin-access-group-grid-mui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/layouts/admin-access-layout.js`:
- Around line 30-36: The current Route path (`path={`${match.url}/:access_id?`}`
in the Switch) is too permissive and treats any single-segment suffix as an edit
route; replace it with explicit routes: add a Route for the "new" page using
`${match.url}/new` (rendering AdminAccessListPage or AdminAccessForm as
appropriate) and add a separate Route for numeric IDs using a constrained param
like `${match.url}/:access_id(\\d+)` that renders AdminAccessListPage for
editing; keep the Redirect to `match.url` after those Routes and ensure the
order is: new route, numeric-id route, then Redirect so invalid segments still
fall through to the redirect.

In `@src/pages/admin_access/admin-access-list-page.js`:
- Around line 63-79: The getAdminAccess(accessId).then(() => setOpen(true)) call
can reopen the modal with stale data; modify the effect around
useEffect/getAdminAccess to guard against late responses by sequencing or
cancellation: capture the current accessId/isNew (from match.params.access_id
and a flag from /new) or create a request token/AbortController before calling
getAdminAccess, and when the promise resolves verify the token matches the
latest accessId (and still not isNew) before calling setOpen(true) and applying
fetched data (or abort the fetch). Also ensure resetAdminAccessForm is only
applied for the intended /new route by checking the same guard.
- Around line 106-112: handleDeleteAdminAccess currently performs an optimistic
delete via deleteAdminAccess but never refetches the paginated data, leaving
currentPage pointing at an empty page after deleting the last item; change the
flow so that after deleteAdminAccess resolves (or in its success
callback/promise then), call the pagination refetch function (e.g.,
fetchAdminAccessPage or refetchAdminAccess) for the currentPage, and if the
returned page is empty and currentPage > 1, decrement currentPage and refetch
the previous page; update the state that holds currentPage and the page data
accordingly instead of relying only on the reducer’s local filter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9c84e8b-8dbe-47a6-856f-4041b0d8ca43

📥 Commits

Reviewing files that changed from the base of the PR and between 08e2650 and 2c47f00.

📒 Files selected for processing (8)
  • src/actions/admin-access-actions.js
  • src/components/forms/admin-access-form.js
  • src/layouts/admin-access-layout.js
  • src/pages/admin_access/__tests__/admin-access-list-page.test.js
  • src/pages/admin_access/admin-access-list-page.js
  • src/pages/admin_access/edit-admin-access-page.js
  • src/reducers/admin_access/__tests__/admin-access-list-reducer.test.js
  • src/reducers/admin_access/admin-access-list-reducer.js
💤 Files with no reviewable changes (1)
  • src/pages/admin_access/edit-admin-access-page.js

Comment thread src/layouts/admin-access-layout.js
Comment on lines +63 to +79
useEffect(() => {
const { access_id: accessId } = match.params;
const isNew = /\/new$/.test(history.location.pathname);

this.state = {};
}
if (isNew) {
resetAdminAccessForm();
setOpen(true);
return;
}

componentDidMount() {
this.props.getAdminAccesses();
}
if (accessId) {
getAdminAccess(accessId).then(() => setOpen(true));
return;
}

handleEdit(admin_access_id) {
const { history } = this.props;
history.push(`/app/admin-access/${admin_access_id}`);
}
setOpen(false);
}, [match.params.access_id, history.location.pathname]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Guard against stale edit loads.

getAdminAccess(accessId).then(() => setOpen(true)) has no stale-request check. If the user closes the dialog or switches from /app/admin-access/1 to /app/admin-access/new before the fetch finishes, the late response can reopen the modal and repopulate the form with the old record. This needs request sequencing/cancellation, or at least a route/id guard before applying the result.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/admin_access/admin-access-list-page.js` around lines 63 - 79, The
getAdminAccess(accessId).then(() => setOpen(true)) call can reopen the modal
with stale data; modify the effect around useEffect/getAdminAccess to guard
against late responses by sequencing or cancellation: capture the current
accessId/isNew (from match.params.access_id and a flag from /new) or create a
request token/AbortController before calling getAdminAccess, and when the
promise resolves verify the token matches the latest accessId (and still not
isNew) before calling setOpen(true) and applying fetched data (or abort the
fetch). Also ensure resetAdminAccessForm is only applied for the intended /new
route by checking the same guard.

Comment thread src/pages/admin_access/admin-access-list-page.js
Comment thread src/actions/admin-access-actions.js Outdated

dispatch(
showMessage(successMessage, () => {
history.push(`/app/admin-access/${payload.response.id}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redirectOnCreate if should go here, if not the noAlert flag is redundant

? `${member.first_name} ${member.last_name} (${member.email})`
: `${member.first_name} ${member.last_name} (${member.id})`;
}}
: `${member.first_name} ${member.last_name} (${member.id})`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the only thing that changes is the parenthesis, then the ternary condition should go there

path={`${match.url}/:access_id?`}
component={AdminAccessListPage}
/>
<Redirect to={match.url} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before the routes would only match on / or digit, or new. Now it matches everything , for example app/admin-access/santi. it is not equivalent to what we had

this.handleDeleteAdminAccess = this.handleDeleteAdminAccess.bind(this);
useEffect(() => {
const { access_id: accessId } = match.params;
const isNew = /\/new$/.test(history.location.pathname);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so what if the url is app/admin-access/santi/new ?

const totalItems =
typeof totalAdminAccesses === "number"
? totalAdminAccesses
: admin_accesses.length;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

admin_accesses is paginated, the length of this will rarely be the total

@priscila-moneo priscila-moneo force-pushed the feature/move-admin-access-group-grid-mui branch from 2c47f00 to e24b38c Compare May 4, 2026 19:53

@santipalenque santipalenque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@priscila-moneo priscila-moneo force-pushed the feature/move-admin-access-group-grid-mui branch 2 times, most recently from ee5e6d9 to c1ff274 Compare May 29, 2026 14:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/pages/admin_access/__tests__/admin-access-list-page.test.js (1)

43-45: ⚡ Quick win

Mock onDelete with the row id, not the full row, to match the real MuiTable contract.

The real MuiTable invokes onDelete with the primitive row id, whereas onEdit receives the full row. This mock fires onDelete(row), so the delete test only exercises the object branch of handleDeleteAdminAccess (typeof rowOrId === "object") while production hits the id branch. Passing row.id keeps the test green and faithful to the contract.

♻️ Align mock with the real callback payload
-          <button type="button" onClick={() => onDelete(row)}>
+          <button type="button" onClick={() => onDelete(row.id)}>
             delete
           </button>

Based on learnings: onDelete is called with the primitive row identifier (e.g., id/rowId), not the full row object, while onEdit and onSelect receive the full row object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/admin_access/__tests__/admin-access-list-page.test.js` around lines
43 - 45, The test mock currently calls onDelete(row) but the real MuiTable
passes the primitive row id; change the mock delete button to call
onDelete(row.id) so the test exercises the id branch in handleDeleteAdminAccess
(keep onEdit as-is since it expects the full row object). Update the mock in the
test file to pass the row.id identifier to onDelete and verify assertions still
target the id-based behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/forms/admin-access-form.js`:
- Around line 33-44: AdminAccessForm currently ignores server-side validation
errors because its props only include { entity, onSubmit, isSaving }; update the
component to accept an errors prop and, inside the component (after useFormik),
watch for changes to that errors prop and call formik.setErrors(errors) and set
relevant fields touched (e.g.,
formik.setTouched(Object.fromEntries(Object.keys(errors||{}).map(k=>[k,true]))))
so Formik displays backend field errors; reference AdminAccessForm, useFormik,
formik.setErrors and formik.setTouched when adding the effect.

---

Nitpick comments:
In `@src/pages/admin_access/__tests__/admin-access-list-page.test.js`:
- Around line 43-45: The test mock currently calls onDelete(row) but the real
MuiTable passes the primitive row id; change the mock delete button to call
onDelete(row.id) so the test exercises the id branch in handleDeleteAdminAccess
(keep onEdit as-is since it expects the full row object). Update the mock in the
test file to pass the row.id identifier to onDelete and verify assertions still
target the id-based behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4f222801-0811-48de-a064-e78c82e90c61

📥 Commits

Reviewing files that changed from the base of the PR and between 2c47f00 and c1ff274.

📒 Files selected for processing (8)
  • src/actions/__tests__/admin-access-actions.test.js
  • src/actions/admin-access-actions.js
  • src/components/forms/admin-access-form.js
  • src/i18n/en.json
  • src/layouts/admin-access-layout.js
  • src/pages/admin_access/__tests__/admin-access-list-page.test.js
  • src/pages/admin_access/admin-access-list-page.js
  • src/pages/admin_access/edit-admin-access-page.js
💤 Files with no reviewable changes (1)
  • src/pages/admin_access/edit-admin-access-page.js
✅ Files skipped from review due to trivial changes (1)
  • src/i18n/en.json

Comment thread src/components/forms/admin-access-form.js Outdated
@priscila-moneo priscila-moneo force-pushed the feature/move-admin-access-group-grid-mui branch from c1ff274 to 9e8890d Compare May 29, 2026 17:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/pages/admin_access/admin-access-list-page.js (2)

77-80: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle getAdminAccess rejection.

The promise chain lacks a .catch. If the fetch fails (e.g. invalid access_id), this produces an unhandled rejection and the dialog silently never opens, leaving the user on a dead URL. Add a catch that closes/returns to the list.

🛡️ Proposed fix
     if (accessId) {
-      getAdminAccess(accessId).then(() => setOpen(true));
+      getAdminAccess(accessId)
+        .then(() => setOpen(true))
+        .catch(() => {
+          history.push("/app/admin-access");
+        });
       return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/admin_access/admin-access-list-page.js` around lines 77 - 80, The
current call to getAdminAccess(accessId).then(() => setOpen(true)) lacks error
handling; wrap it with a .catch to handle rejection by closing the dialog and
returning the user to the list. Specifically, update the branch that calls
getAdminAccess(accessId) so failures call setOpen(false) (or ensure the dialog
remains closed) and perform a navigation back to the admin-access list (e.g.,
via the app's router helper or history push/replace) and/or show a user-facing
error; keep getAdminAccess and setOpen as the referenced symbols when
implementing the .catch handler.

150-162: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add a render formatter for the summits/members columns

getAdminAccesses expands summits/members and requests summits.name plus members.first_name/last_name, so these columnKeys receive arrays of objects. Since the columns define no render formatter, the table renders the raw row[col.columnKey] (invalid React children / [object Object]). Add a render that maps to display strings.

🐛 Proposed fix (confirm the column renderer prop name for MuiTable)
       { columnKey: "title", header: T.translate("admin_access.title"), sortable: true },
-      { columnKey: "summits", header: T.translate("admin_access.summits") },
-      { columnKey: "members", header: T.translate("admin_access.members") }
+      {
+        columnKey: "summits",
+        header: T.translate("admin_access.summits"),
+        render: (row) => (row.summits || []).map((s) => s.name).join(", ")
+      },
+      {
+        columnKey: "members",
+        header: T.translate("admin_access.members"),
+        render: (row) =>
+          (row.members || [])
+            .map((m) => `${m.first_name} ${m.last_name}`)
+            .join(", ")
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/admin_access/admin-access-list-page.js` around lines 150 - 162, The
summits and members table columns in the columns array created in useMemo lack
render formatters, so the table tries to render arrays of objects (causing
[object Object] or invalid React children); update the column definitions for
columnKey "summits" and "members" to add a render function (or the MuiTable
column renderer prop name used in this codebase) that maps each summit object to
summit.name (joined by commas) and each member object to a display name like
`${first_name} ${last_name}` (also joined by commas) before returning the
string/JSX for the cell; locate the columns definition in
admin-access-list-page.js (the useMemo that defines columns) and add these
renderers to those two column entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/forms/admin-access-form.js`:
- Around line 84-88: The getOptionLabel callback uses
member.hasOwnProperty("email"), which triggers no-prototype-builtins warnings
and can fail for objects with a null prototype or shadowed methods; update the
check in getOptionLabel to use Object.prototype.hasOwnProperty.call(member,
"email") (or an equivalent safe check like Boolean(member.email)) so the label
generation for the member (first_name, last_name, email/id) remains identical
but avoids prototype-builtins issues.

---

Outside diff comments:
In `@src/pages/admin_access/admin-access-list-page.js`:
- Around line 77-80: The current call to getAdminAccess(accessId).then(() =>
setOpen(true)) lacks error handling; wrap it with a .catch to handle rejection
by closing the dialog and returning the user to the list. Specifically, update
the branch that calls getAdminAccess(accessId) so failures call setOpen(false)
(or ensure the dialog remains closed) and perform a navigation back to the
admin-access list (e.g., via the app's router helper or history push/replace)
and/or show a user-facing error; keep getAdminAccess and setOpen as the
referenced symbols when implementing the .catch handler.
- Around line 150-162: The summits and members table columns in the columns
array created in useMemo lack render formatters, so the table tries to render
arrays of objects (causing [object Object] or invalid React children); update
the column definitions for columnKey "summits" and "members" to add a render
function (or the MuiTable column renderer prop name used in this codebase) that
maps each summit object to summit.name (joined by commas) and each member object
to a display name like `${first_name} ${last_name}` (also joined by commas)
before returning the string/JSX for the cell; locate the columns definition in
admin-access-list-page.js (the useMemo that defines columns) and add these
renderers to those two column entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8e636853-e8b9-4f12-9db6-0f33466cbd6e

📥 Commits

Reviewing files that changed from the base of the PR and between c1ff274 and 9e8890d.

📒 Files selected for processing (7)
  • src/actions/__tests__/admin-access-actions.test.js
  • src/actions/admin-access-actions.js
  • src/components/forms/admin-access-form.js
  • src/i18n/en.json
  • src/layouts/admin-access-layout.js
  • src/pages/admin_access/__tests__/admin-access-list-page.test.js
  • src/pages/admin_access/admin-access-list-page.js
✅ Files skipped from review due to trivial changes (1)
  • src/i18n/en.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/layouts/admin-access-layout.js
  • src/pages/admin_access/tests/admin-access-list-page.test.js
  • src/actions/tests/admin-access-actions.test.js

Comment on lines +84 to +88
getOptionLabel={(member) =>
member.hasOwnProperty("email")
? `${member.first_name} ${member.last_name} (${member.email})`
: `${member.first_name} ${member.last_name} (${member.id})`;
}}
onChange={this.handleChange}
multi={true}
: `${member.first_name} ${member.last_name} (${member.id})`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -H -t f -g '.eslintrc*' | xargs -r cat
rg -nP "no-prototype-builtins"
rg -nP "\.hasOwnProperty\(" --type=js -g '!**/node_modules/**'

Repository: fntechgit/summit-admin

Length of output: 31424


Avoid calling member.hasOwnProperty("email") in getOptionLabel.

no-prototype-builtins is enabled in this repo (set to "warn"), and this exact pattern also appears in other files. Using Object.prototype.hasOwnProperty.call avoids issues with null-prototype/shadowed hasOwnProperty and keeps lint clean.

♻️ Proposed fix
-              getOptionLabel={(member) =>
-                member.hasOwnProperty("email")
+              getOptionLabel={(member) =>
+                Object.prototype.hasOwnProperty.call(member, "email")
                   ? `${member.first_name} ${member.last_name} (${member.email})`
                   : `${member.first_name} ${member.last_name} (${member.id})`
               }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getOptionLabel={(member) =>
member.hasOwnProperty("email")
? `${member.first_name} ${member.last_name} (${member.email})`
: `${member.first_name} ${member.last_name} (${member.id})`;
}}
onChange={this.handleChange}
multi={true}
: `${member.first_name} ${member.last_name} (${member.id})`
}
getOptionLabel={(member) =>
Object.prototype.hasOwnProperty.call(member, "email")
? `${member.first_name} ${member.last_name} (${member.email})`
: `${member.first_name} ${member.last_name} (${member.id})`
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/forms/admin-access-form.js` around lines 84 - 88, The
getOptionLabel callback uses member.hasOwnProperty("email"), which triggers
no-prototype-builtins warnings and can fail for objects with a null prototype or
shadowed methods; update the check in getOptionLabel to use
Object.prototype.hasOwnProperty.call(member, "email") (or an equivalent safe
check like Boolean(member.email)) so the label generation for the member
(first_name, last_name, email/id) remains identical but avoids
prototype-builtins issues.

this.props.getAdminAccesses();
}
if (accessId) {
getAdminAccess(accessId).then(() => setOpen(true));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAdminAccess(accessId) has no .catch() here. If the API returns an error (404, network failure, auth expiry), the dialog never opens and the URL stays at /app/admin-access/:id with no recovery path. authErrorHandler shows a toast but does not navigate back to the list.

Suggested fix: add .catch(() => history.push("/app/admin-access")) so a failed load redirects back to the list.

const [searchTerm, setSearchTerm] = useState(term || "");
const [open, setOpen] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const isSavingRef = useRef(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deviates from the project's popup/dialog pattern (summit-admin-popup-dialog-pattern.md), which uses only useState for the saving guard. In React 16, closeDialog is a fresh closure on every render and already sees the current isSaving value — the ref adds no correctness benefit but creates two sources of truth that can drift if the component unmounts mid-save.

Suggested fix: remove isSavingRef and check isSaving directly in closeDialog, consistent with every other popup in the codebase.

});

export default connect(mapStateToProps, {
getSummitById,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getSummitById is wired into connect but neither destructured in the component's props nor called anywhere — missed cleanup from the refactor. Safe to remove from the import and the connect call.

);
});
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests cover the happy-path dispatch sequence (ADDED/UPDATED → STOP_LOADING) but there is no test for the save failure path. A test that rejects postRequest/putRequest would confirm that stopLoading still fires via .finally() — a future refactor could silently break that guarantee.

mockSaveAdminAccess.mockReturnValue(() => Promise.resolve());
});

describe("rendering and navigation", () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing coverage for several failure/navigation scenarios:

  • getAdminAccess failure path: when mockGetAdminAccess rejects, the dialog should not open and the component should navigate back to /app/admin-access.
  • saveAdminAccess rejection path: verifying stopLoading fires via .finally() is tested in the action suite, but the component-level behavior (dialog stays open, buttons re-enabled, list does not refresh) has no equivalent test here.
  • URL effect reset: no test verifies that navigating away from a detail route (e.g. from /app/admin-access/1 back to /app/admin-access) closes the dialog.

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@priscila-moneo please review comments

@priscila-moneo priscila-moneo force-pushed the feature/move-admin-access-group-grid-mui branch from 9e8890d to 90f9d74 Compare June 12, 2026 19:44
@priscila-moneo priscila-moneo requested a review from smarcet June 12, 2026 19:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/admin_access/__tests__/admin-access-list-page.test.js`:
- Around line 104-109: The test currently never sets baseState.loading so the
"in-flight save disables controls" assertions don't exercise the real guard;
update the test to drive loading by either (a) calling renderPage with a store
state where baseState.loading is > 0 (e.g. 1) to simulate an in-flight save, or
(b) mock the save thunk to dispatch the loading start action before resolving so
the component sees loading > 0; locate renderPage and mockStore usage and the
save thunk mock to implement one of these changes (adjust renderPage invocation
or the thunk mock) so the disabled-state assertions run against an actual
loading state.

In `@src/pages/admin_access/admin-access-form-popup.js`:
- Around line 1-2: The component currently relies on the global loading prop
which is set after an async token fetch, allowing rapid repeated submits; add a
component-scoped in-flight guard (e.g., local state boolean isSaving or
isSubmitting) and use it at the start of the submit handler to short-circuit
duplicate calls to saveAdminAccess; set isSaving=true immediately before
initiating the async/token fetch, reset it in a finally block after
saveAdminAccess completes or errors, and keep the existing checks against the
global loading prop (reference the submit handler that calls saveAdminAccess,
the saveAdminAccess dispatch, and the loading prop) so multiple rapid clicks
cannot trigger concurrent saves.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47d29561-f28d-407e-8618-63bf77691dd6

📥 Commits

Reviewing files that changed from the base of the PR and between 9e8890d and 90f9d74.

📒 Files selected for processing (11)
  • src/actions/__tests__/admin-access-actions.test.js
  • src/actions/admin-access-actions.js
  • src/components/forms/admin-access-form.js
  • src/i18n/en.json
  • src/layouts/admin-access-layout.js
  • src/pages/admin_access/__tests__/admin-access-list-page.test.js
  • src/pages/admin_access/admin-access-form-popup.js
  • src/pages/admin_access/admin-access-list-page.js
  • src/pages/admin_access/edit-admin-access-page.js
  • src/reducers/admin_access/__tests__/admin-access-list-reducer.test.js
  • src/reducers/admin_access/admin-access-list-reducer.js
💤 Files with no reviewable changes (1)
  • src/pages/admin_access/edit-admin-access-page.js
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/reducers/admin_access/tests/admin-access-list-reducer.test.js
  • src/i18n/en.json
  • src/actions/tests/admin-access-actions.test.js
  • src/layouts/admin-access-layout.js
  • src/components/forms/admin-access-form.js
  • src/reducers/admin_access/admin-access-list-reducer.js
  • src/actions/admin-access-actions.js

Comment thread src/pages/admin_access/__tests__/admin-access-list-page.test.js
Comment thread src/pages/admin_access/admin-access-form-popup.js Outdated
@priscila-moneo priscila-moneo force-pushed the feature/move-admin-access-group-grid-mui branch 2 times, most recently from a8ecc81 to cf34c55 Compare June 12, 2026 20:32

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@priscila-moneo there are UX issues and we need to get rid of the sweet alert
in favor of snackbar handler

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for three /sort/search paths identified in review.

);

return MockMuiTable;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing: handleSort not reachable from this mock.

The MockMuiTable destructures onEdit, onDelete, onPerPageChange but omits onSort. No test can exercise handleSortgetAdminAccesses(term, currentPage, perPage, key, dir) without exposing it here.

Suggested addition to the mock:

<button type="button" onClick={() => onSort('title', -1)}>
  sort-col
</button>

And a corresponding test asserting mockGetAdminAccesses was called with the new sort column/direction.

__esModule: true,
default: () => <input placeholder="search" />
})
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing: handleSearch is never triggered from this mock.

The MuiSearchInput mock renders a plain <input> but doesn't wire up the onSearch prop, so handleSearchgetAdminAccesses(value, DEFAULT_CURRENT_PAGE, ...) is never exercised.

Suggested addition to the mock:

default: ({ onSearch }) => (
  <input placeholder="search" onChange={(e) => onSearch(e.target.value)} />
)

And a test typing into the input and asserting mockGetAdminAccesses was called with the new search term and page reset to 1.

expect(mockGetAdminAccesses.mock.calls.length).toBeGreaterThan(
callsBefore
);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing: nextPage decrement branch never triggered.

The production handleDeleteAdminAccess has this branch:

const nextPage =
  admin_accesses.length === 1 && currentPage > 1
    ? currentPage - 1
    : currentPage;

Both existing delete tests use baseListState where currentPage: 1, so the decrement path (currentPage - 1) is never reached.

Suggested test to add after this one:

it('decrements page when deleting the last item on a page > 1', async () => {
  const store = mockStore({
    adminAccessListState: { ...baseListState, currentPage: 2, admin_accesses: [{ id: 1, title: 'G' }] },
    adminAccessState: baseFormState
  });
  // render, click delete, flush
  // assert mockGetAdminAccesses was last called with page = 1
});

const handleSave = (adminAccessEntity) => {
if (isSaving) return;
setIsSaving(true);
saveAdminAccess(adminAccessEntity)

@smarcet smarcet Jun 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deviates from the dominant codebase pattern. Surveying the codebase, 6 out of 7 popup implementations keep isSaving in the popup (correct here), but the save action and the post-save reload chain belong in the parent list page — not inside the popup.

The pattern used by media-file-type-list-page, form-template-list-page, and others:

// AdminAccessListPage — parent owns the save + reload chain
const handleSave = (entity) =>
  saveAdminAccess(entity)
    .then(() => getAdminAccesses(term, currentPage, perPage, order, orderDir));

// passed as a single prop
<AdminAccessFormPopup onSave={handleSave} onClose={closeDialog} />
// AdminAccessFormPopup — popup only calls onSave and handles isSaving
const handleSave = (entity) => {
  if (isSaving) return;
  setIsSaving(true);
  onSave(entity)
    .then(() => onClose())
    .catch(() => {})
    .finally(() => setIsSaving(false));
};

With this shape: saveAdminAccess and its Redux connect move to the parent, the popup receives a single onSave prop (instead of onSaved + onClose), and isSaving stays in the popup — which is correct per the dominant pattern.

Signed-off-by: Priscila Moneo <priscila_moneo@hotmail.com.ar>
@priscila-moneo priscila-moneo force-pushed the feature/move-admin-access-group-grid-mui branch from cf34c55 to 4a3b2e5 Compare June 18, 2026 18:28
@priscila-moneo priscila-moneo requested a review from smarcet June 18, 2026 18:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/actions/admin-access-actions.js`:
- Around line 94-95: The snackbarErrorHandler may be swallowing promise
rejections instead of propagating them, causing error handling chains to break.
In the getAdminAccess and deleteAdminAccess functions, replace the `.then(() =>
stopLoading())` pattern with a `.finally(() => stopLoading())` block to ensure
the loading state is cleared regardless of request success or failure, similar
to how saveAdminAccess already implements this pattern. Verify that
snackbarErrorHandler explicitly returns a rejected Promise after displaying the
error message so that downstream catch handlers in AdminAccessListPage and other
callers can properly handle the rejection chain.

In `@src/components/forms/admin-access-form.js`:
- Around line 51-57: The useEffect hook in admin-access-form.js returns early
when serverErrors is empty without clearing previously set Formik errors,
causing stale error messages to persist even after Redux resets the errors.
Instead of returning early when serverErrors is empty, call formik.setErrors({})
and formik.setTouched({}) to explicitly clear Formik's error and touched state,
ensuring it stays synchronized with the Redux state being reset.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b74d7e8a-5671-49b0-bc6b-bb914b8be1ad

📥 Commits

Reviewing files that changed from the base of the PR and between 90f9d74 and 4a3b2e5.

📒 Files selected for processing (11)
  • src/actions/__tests__/admin-access-actions.test.js
  • src/actions/admin-access-actions.js
  • src/components/forms/admin-access-form.js
  • src/i18n/en.json
  • src/layouts/admin-access-layout.js
  • src/pages/admin_access/__tests__/admin-access-list-page.test.js
  • src/pages/admin_access/admin-access-form-popup.js
  • src/pages/admin_access/admin-access-list-page.js
  • src/pages/admin_access/edit-admin-access-page.js
  • src/reducers/admin_access/__tests__/admin-access-list-reducer.test.js
  • src/reducers/admin_access/admin-access-list-reducer.js
💤 Files with no reviewable changes (1)
  • src/pages/admin_access/edit-admin-access-page.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/reducers/admin_access/tests/admin-access-list-reducer.test.js
  • src/i18n/en.json
  • src/actions/tests/admin-access-actions.test.js
  • src/layouts/admin-access-layout.js
  • src/reducers/admin_access/admin-access-list-reducer.js

Comment on lines +94 to +95
snackbarErrorHandler,
{ order, orderDir, term, page, perPage }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1) Inspect snackbar/auth error handler implementations.
fd -i 'base-actions.js' | xargs -r rg -n -A20 -B5 'snackbarErrorHandler|authErrorHandler'

# 2) Inspect request helper semantics (resolve vs reject on errors).
rg -n -A30 -B10 --type=js 'export const getRequest|function getRequest|const getRequest'
rg -n -A30 -B10 --type=js 'export const postRequest|export const putRequest|export const deleteRequest'

# 3) Re-check caller expectation in list page.
rg -n -A8 -B4 --type=js 'getAdminAccess\(' src/pages/admin_access/admin-access-list-page.js

Repository: fntechgit/summit-admin

Length of output: 2009


🏁 Script executed:

# Check request-helper or actions utility to understand error handler contract
rg -n -A15 'error.*handler|errorHandler' src/utils/request-helper.js src/utils/actions.js 2>/dev/null | head -100

# Inspect the actual admin-access-actions.js to see full context of lines 94-95, 118-119, 190
cat -n src/actions/admin-access-actions.js | sed -n '85,110p'
cat -n src/actions/admin-access-actions.js | sed -n '110,130p'
cat -n src/actions/admin-access-actions.js | sed -n '185,195p'

# Find how request utilities handle error callbacks (getRequest, postRequest, etc.)
fd -type f 'request-helper.js' | xargs -r cat -n | head -150

Repository: fntechgit/summit-admin

Length of output: 2321


🏁 Script executed:

# Find request-helper or actions utility files
find . -name "*request*" -o -name "*actions*" | grep -E "utils|lib" | head -20

# Look for getRequest implementation to understand error handler contract
rg -n "export.*getRequest|function getRequest" | head -20

# Check other error handler implementations for comparison
rg -n "ErrorHandler.*=" -A5 | head -100

Repository: fntechgit/summit-admin

Length of output: 5481


🏁 Script executed:

# Check package.json for openstack-uicore-foundation version
cat package.json | grep -A2 -B2 "openstack-uicore-foundation"

# Look for any local request helper/wrapper that might provide more context
find src -name "*request*" -o -name "*helper*" | grep -v node_modules

# Check admin-access-list-page to understand caller expectations
cat -n src/pages/admin_access/admin-access-list-page.js | sed -n '55,80p'

Repository: fntechgit/summit-admin

Length of output: 1720


🏁 Script executed:

# Look for any error handlers that explicitly return rejected promises or throw
rg -n "return.*reject|throw.*Error|Promise.reject" src/actions/ | head -30

# Check if any error handlers dispatch a rejection or error state
rg -n "ERROR|FAILURE" src/actions/admin-access-actions.js

# Look for patterns where handlers might suppress errors
rg -n "catch.*=>" src/actions/ | head -20

Repository: fntechgit/summit-admin

Length of output: 2140


🌐 Web query:

openstack-uicore-foundation getRequest error handler Promise rejection semantics

💡 Result:

The library openstack-uicore-foundation is a collection of ReactJS and Redux components designed for the OpenStack marketing website [1][2]. It is not a general-purpose OpenStack API client or a low-level SDK for managing OpenStack infrastructure [1][2]. There is no evidence of a "getRequest" function or specific "Promise rejection semantics" within the openstack-uicore-foundation repository [1][2]. Queries regarding "getRequest" and error handling in an OpenStack context typically relate to other projects, such as: - The OpenStack SDK (for Python, Rust, or other languages), which provides standardized methods for API interaction and error handling [3][4][5]. - OpenStack API specifications regarding structured error responses [6]. - Other frameworks like Uvicore (a separate Python framework) that explicitly manage exception handlers [7]. If you are encountering a "getRequest" error, please verify the library or SDK you are using to interact with the OpenStack API, as it is likely not openstack-uicore-foundation [1][2][3]. For standard JavaScript/TypeScript applications interacting with OpenStack APIs, standard Promise-based error handling (using.catch or try/catch blocks with async/await) is the recommended approach to managing rejections [1][2].

Citations:


🏁 Script executed:

# Check admin-access-actions test file for mocking patterns
cat -n src/actions/__tests__/admin-access-actions.test.js | head -80

# Look for other uses of getRequest to see error handler patterns
rg -n "getRequest" src/actions/ -B3 -A3 | head -150

# Check if there's documentation or type definitions for getRequest
find . -path ./node_modules -prune -o -name "*.d.ts" -exec grep -l "getRequest" {} \; 2>/dev/null | head -10

Repository: fntechgit/summit-admin

Length of output: 11707


🏁 Script executed:

# Get the complete saveAdminAccess function
cat -n src/actions/admin-access-actions.js | sed -n '128,175p'

Repository: fntechgit/summit-admin

Length of output: 1723


🏁 Script executed:

# Get the complete test case that checks rejection behavior
cat -n src/actions/__tests__/admin-access-actions.test.js | sed -n '77,95p'

Repository: fntechgit/summit-admin

Length of output: 837


🏁 Script executed:

# Search for authErrorHandler implementation in the codebase
rg -n "export const authErrorHandler|export function authErrorHandler" -A10

# Also check if there's a local implementation or if it's all from openstack-uicore-foundation
rg -n "authErrorHandler" src/ --type=js | head -20

Repository: fntechgit/summit-admin

Length of output: 1273


Confirm error handler doesn't suppress rejections in the request chain.

snackbarErrorHandler dispatches a message but doesn't explicitly return a rejected Promise. Verify that getRequest, putRequest, and deleteRequest from openstack-uicore-foundation properly re-throw or re-reject after invoking the error handler; otherwise:

  • AdminAccessListPage relies on getAdminAccess(accessId).catch(...) to redirect on failure, which won't trigger if the rejection is swallowed
  • getAdminAccess and deleteAdminAccess use .then(() => stopLoading()) which won't execute on error, leaving loading state stuck

Consider using .finally() pattern (as in saveAdminAccess) to ensure stopLoading() always executes regardless of request outcome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/actions/admin-access-actions.js` around lines 94 - 95, The
snackbarErrorHandler may be swallowing promise rejections instead of propagating
them, causing error handling chains to break. In the getAdminAccess and
deleteAdminAccess functions, replace the `.then(() => stopLoading())` pattern
with a `.finally(() => stopLoading())` block to ensure the loading state is
cleared regardless of request success or failure, similar to how saveAdminAccess
already implements this pattern. Verify that snackbarErrorHandler explicitly
returns a rejected Promise after displaying the error message so that downstream
catch handlers in AdminAccessListPage and other callers can properly handle the
rejection chain.

Comment on lines +51 to +57
useEffect(() => {
if (!serverErrors || Object.keys(serverErrors).length === 0) return;
formik.setErrors(serverErrors);
formik.setTouched(
Object.fromEntries(Object.keys(serverErrors).map((k) => [k, true]))
);
}, [serverErrors]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clear Formik server errors when Redux errors reset.

Line 52 exits early for empty serverErrors, so previously injected backend errors can remain visible in Formik even after Redux clears errors back to {}.

♻️ Proposed fix
   useEffect(() => {
-    if (!serverErrors || Object.keys(serverErrors).length === 0) return;
-    formik.setErrors(serverErrors);
+    const nextErrors = serverErrors || {};
+    formik.setErrors(nextErrors);
+    if (Object.keys(nextErrors).length === 0) return;
     formik.setTouched(
-      Object.fromEntries(Object.keys(serverErrors).map((k) => [k, true]))
+      Object.fromEntries(Object.keys(nextErrors).map((k) => [k, true]))
     );
   }, [serverErrors]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/forms/admin-access-form.js` around lines 51 - 57, The
useEffect hook in admin-access-form.js returns early when serverErrors is empty
without clearing previously set Formik errors, causing stale error messages to
persist even after Redux resets the errors. Instead of returning early when
serverErrors is empty, call formik.setErrors({}) and formik.setTouched({}) to
explicitly clear Formik's error and touched state, ensuring it stays
synchronized with the Redux state being reset.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants